package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"strconv"
	"time"
	"usplatformserver/global"
	"usplatformserver/internal/model"
	"usplatformserver/internal/routers"
	"usplatformserver/pkg/app"
	"usplatformserver/pkg/commondata"
	"usplatformserver/pkg/logger"
	"usplatformserver/pkg/mqtt"
	"usplatformserver/pkg/setting"

	"usplatformserver/pkg/zmq"

	"github.com/gofrs/uuid"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	isVersion bool
	buildTime string
	goVersion string
)

func setupSetting() error {
	setting, err := setting.NewSetting()
	if err != nil {
		return err
	}
	err = setting.ReadSection("Database", &global.DBSettings)
	if err != nil {
		return err
	}
	err = setting.ReadSection("App", &global.AppSettings)
	if err != nil {
		return err
	}
	err = setting.ReadSection("Server", &global.ServerSettings)
	if err != nil {
		return err
	} else {
		global.ServerSettings.ReadTimeout *= time.Second
		global.ServerSettings.WriteTimeout *= time.Second
	}
	err = setting.ReadSection("JWT", &global.JWTSettings)
	if err != nil {
		return err
	} else {
		global.JWTSettings.Expire *= time.Second
		global.JWTSettings.Issuer = "proxima"
		global.JWTSettings.AppSecret = "proxima-us"
	}
	err = setting.ReadSection("ZMQ", &global.ZMQSettings)
	if err != nil {
		return err
	}
	err = setting.ReadSection("Mosquitto", &global.MosqtSettings)
	if err != nil {
		return err
	}

	return nil
}

func setupDBEngine() error {
	var err error
	global.DBEngine, err = model.NewDBEngine(global.DBSettings)
	if err != nil {
		return err
	}
	return nil
}

func setupLogger() error {
	global.Logger = logger.NewLogger(&lumberjack.Logger{
		Filename:  global.AppSettings.LogSavePath + "/" + global.AppSettings.LogFileName + global.AppSettings.LogFileExt,
		MaxSize:   60,
		MaxAge:    30,
		LocalTime: true,
	}, "", log.LstdFlags).WithCaller(2)

	return nil
}

func setupFlag() error {
	flag.BoolVar(&isVersion, "version", false, "编译信息")
	flag.Parse()
	buildTime = app.GetBuildTime()
	goVersion = runtime.Version()
	return nil
}

// 删除导出文件缓存
func removeExportFileCache() {
	strCmd := "rm -rf *.xlsx"
	cmd := exec.Command("bash", "-c", strCmd)
	err := cmd.Start()
	if err != nil {
		global.Logger.Infof("remove export file failed: %v", err)
	}
}

func setupMqtt() {
	var clientId string = "go_mqtt_server"
	uid, err := uuid.NewV1()
	if nil == err {
		clientId = uid.String()
	}
	fmt.Println("clientId: ", clientId)
	// MQTT
	mqttOpts := mqtt.MqttConnectConfig{
		Broker:   fmt.Sprintf("tcp://%s:%d", global.MosqtSettings.Addr, global.MosqtSettings.Port),
		Qos:      2,
		Retained: false,
		ClientId: clientId,
		User:     "aitrox",
		Password: "aitrox",
	}
	if nil == global.MqttClient {
		global.MqttClient = mqtt.NewMqttClient(mqttOpts)
	}
	if nil != global.MqttClient {
		fmt.Println("global.MqttClient is not nil")
		if err := global.MqttClient.Connect(); err != nil {
			fmt.Println("MQTT连接失败")
		}
	}

}

func releaseMqtt() {
	if nil != global.MqttClient {
		global.Logger.Infof("MQTT %s dissconnect", global.MqttClient.ID)
		global.MqttClient.Disconnect()
	}
}

func init() {
	err := setupSetting()
	if err != nil {
		log.Fatalf("init.setupSettings err: %v", err)
	}
	err = setupDBEngine()
	if err != nil {
		log.Fatalf("init.setupDBEngine err: %v", err)
	}
	err = setupLogger()
	if err != nil {
		log.Fatalf("init.setupLogger err: %v", err)
	}
	//解析输入
	setupFlag()
}

func main() {
	//查看版本
	if isVersion {
		fmt.Printf("build_time: %s\n", buildTime)
		fmt.Printf("build_version: %s\n", app.AppVersion)
		fmt.Printf("go_version: %s\n", goVersion)
		return
	}
	//删除导出文件缓存
	removeExportFileCache()

	//初始化待下载文件个数
	commondata.New().InitWaitDownloadCount()

	router := routers.NewRouter()

	s := &http.Server{
		Addr:           ":" + strconv.Itoa(global.ServerSettings.HttpPort),
		Handler:        router,
		ReadTimeout:    global.ServerSettings.ReadTimeout,
		WriteTimeout:   global.ServerSettings.WriteTimeout,
		MaxHeaderBytes: 1 << 20,
	}

	global.Logger.Infof("Listening: %v", global.ServerSettings.HttpPort)

	go func() {
		if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			global.Logger.Fatalf("s.ListenAndServe err: %v", err)
		}
	}()

	//加载MQTT
	go setupMqtt()

	//启动zmq subscribe监听
	_, subsock := zmq.Subscriber(global.ZMQSettings.PubIP, global.ZMQSettings.PubPort)
	go zmq.Listener(subsock)

	//等待中断信号
	quit := make(chan os.Signal)
	// 接收syscall.SIGINT 和 syscall.SIGTERM信号
	signal.Notify(quit, os.Interrupt)
	<-quit
	global.Logger.Info("Shuting down server......")

	//释放MQTT
	releaseMqtt()

	//释放zmq subscriber监听
	subsock.Close()

	//最大时间控制，通知服务端它有10s的时间来处理原有请求
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := s.Shutdown(ctx); err != nil {
		global.Logger.Fatalf("Server forced to shutdown: %v", err)
	}

	global.Logger.Info("Server exiting")
}
